import json
import os
import time

import allure
import pytest
from filelock import FileLock

from selenium import webdriver
from selenium.webdriver import Remote
from selenium.webdriver.chrome.options import Options as CH_Options
from selenium.webdriver.firefox.options import Options as FF_Options
from common.config import RunConfig, PathConfig
from page.login_page import LoginPage
from tools.assert_tool.assert_control import assert_compare
from tools.read_file_rool.cache_control import CacheControl
from poium.common import logging
from tools.read_file_rool.yaml_control import YamlControl

root_path = PathConfig.root_path
cache_path = PathConfig.cache_path
cookies_path = os.path.join(cache_path, "cookies")
base_url = RunConfig.url
case_result = None
username = YamlControl().get_yaml_data("$.account.username")
password = YamlControl().get_yaml_data("$.account.password")


# 项目首页URL
@pytest.fixture(scope='function')
def url():
    return base_url


def clear_cache():
    """
    清空缓存文件夹
    """
    cache_list = os.listdir(cache_path)
    for cache in cache_list:
        path = os.path.join(cache_path, cache)
        os.remove(path)


def login(browser, url):
    """
    登录函数
    """
    try:
        page = LoginPage(browser)
        page.open(url)
        page.login_a.click()
        page.username_input = username
        page.password_input = password
        page.login_span.click()
        assert_compare(page.username_span.text, '==', username)
        cookies = page.get_cookies()
        browser.quit()
    except Exception as e:
        pytest.exit("登录失败")
        raise e
    return cookies


@pytest.fixture(scope="session", autouse=True)
def session_fixture(tmp_path_factory, worker_id):
    # 如果是单机运行 则运行这里的代码块【不可删除、修改】
    if worker_id == "master":
        """
        【自定义代码块】
        这里就写你要本身应该要做的操作，比如：登录请求、新增数据、清空数据库历史数据等等
        """
        cookies = login(get_driver(), base_url)
        CacheControl("cookies").cache = str(cookies)

    # 如果是分布式运行
    else:
        # 获取所有子节点共享的临时目录，无需修改【不可删除、修改】
        root_tmp_dir = tmp_path_factory.getbasetemp().parent
        # 【不可删除、修改】
        fn = root_tmp_dir / "data.json"
        # 【不可删除、修改】
        with FileLock(str(fn) + ".lock"):
            # 【不可删除、修改】
            if fn.is_file():
                # 缓存文件中读取数据，像登录操作的话就是 token 【不可删除、修改】
                cookies = json.loads(fn.read_text())
            else:
                """
                【自定义代码块】
                跟上面 if 的代码块一样就行
                """
                clear_cache()
                cookies = login(get_driver(), base_url)
                # 【不可删除、修改】
                fn.write_text(json.dumps(cookies))

            # 最好将后续需要保留的数据存在缓存文件
            CacheControl("cookies").cache = str(cookies)


@pytest.fixture(scope='function', autouse=True)
def set_up_function(browser):
    # 用例执行前获取cookies缓存
    cookies = CacheControl("cookies").cache
    browser.get(base_url)
    for i in eval(cookies):
        browser.add_cookie(i)
    browser.refresh()
    time.sleep(2)


# 控制浏览器启动和关闭
@pytest.fixture(scope='function')
def browser():
    global d
    d = get_driver()
    RunConfig.driver = d
    yield d
    d.quit()


def get_driver():
    """
    全局定义浏览器驱动
    :return:
    """
    if RunConfig.driver_type == "chrome":
        # 本地chrome浏览器
        driver = webdriver.Chrome()
        driver.maximize_window()

    elif RunConfig.driver_type == "firefox":
        # 本地firefox浏览器
        driver = webdriver.Firefox()
        driver.maximize_window()

    elif RunConfig.driver_type == "chrome-headless":
        # chrome headless模式
        chrome_options = CH_Options()
        chrome_options.add_argument("--headless")
        chrome_options.add_argument('--disable-gpu')
        chrome_options.add_argument("--window-size=1920x1080")
        driver = webdriver.Chrome(options=chrome_options)

    elif RunConfig.driver_type == "firefox-headless":
        # firefox headless模式
        firefox_options = FF_Options()
        firefox_options.headless = True
        driver = webdriver.Firefox(firefox_options=firefox_options)

    elif RunConfig.driver_type == "grid":
        # 通过远程节点运行
        driver = Remote(command_executor='http://localhost:4444/wd/hub',
                        desired_capabilities={
                              "browserName": "chrome",
                        })
        driver.set_window_size(1920, 1080)

    else:
        raise NameError("driver驱动类型定义错误！")

    return driver


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """
    hook pytest失败
    :param item:
    :param call:
    :return:
    """
    global case_result
    global item_name
    # 执行所有其他钩子以获取报告对象
    outcome = yield
    rep = outcome.get_result()
    if rep.when == "call":
        case_result = rep.outcome
    item_name = item.name
    # 只关注实际失败的测试调用, 不关心setup/teardown
    if rep.when == "call" and rep.failed:
        mode = "a" if os.path.exists("../../failures") else "w"
        with open(root_path+"failures", mode) as f:
            if "tmpdir" in item.fixturenames:
                extra = " (%s)" % item.funcargs["tmpdir"]
            else:
                extra = ""
            f.write(rep.nodeid + extra + "\n")
        with allure.step('添加失败截图...'):
            allure.attach(d.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)


@pytest.fixture(scope="function", autouse=True)
def print_request(request):
    split_line = "="*40
    logging.info(split_line+request.function.__name__+split_line)
    logging.info("用例路径："+str(request.node.nodeid))
    logging.info("使用夹具："+str(request.fixturenames))
    logging.info(split_line+'steps'+split_line)
    yield
    logging.info(split_line+'case_result'+split_line+"\n")


def pytest_terminal_summary(terminalreporter):
    """
    收集测试结果
    """
    _PASSED = len([i for i in terminalreporter.stats.get('passed', []) if i.when != 'teardown'])
    _ERROR = len([i for i in terminalreporter.stats.get('error', []) if i.when != 'teardown'])
    _FAILED = len([i for i in terminalreporter.stats.get('failed', []) if i.when != 'teardown'])
    _SKIPPED = len([i for i in terminalreporter.stats.get('skipped', []) if i.when != 'teardown'])
    _TOTAL = terminalreporter._numcollected
    _TIMES = time.time() - terminalreporter._sessionstarttime
    logging.info(""+"="*50+"自动化测试结束！"+"="*50)
    logging.info(f"成功用例数: {_PASSED}")
    logging.error(f"异常用例数: {_ERROR}")
    logging.error(f"失败用例数: {_FAILED}")
    logging.warning(f"跳过用例数: {_SKIPPED}")
    logging.info("用例执行时长: %.2f" % _TIMES + " s")

    # try:
    #     _RATE = round(_PASSED / _TOTAL * 100, 2)
    #     logging.info("用例成功率: %.2f" % _RATE + " %")
    # except ZeroDivisionError:
    #     logging.info("用例成功率: 0.00 %")


# @pytest.fixture(autouse=True)
# def reset():
#     """
#     每条用例执行后，根据运行结果处理
#     """
#     print()  # 控制台打印与用例路径换行
#     yield
#     if case_result != 'passed':
#         driver.get(base_url)
#         driver.refresh()


# # 通过fixture调用上一个用例的运行结果
# @pytest.fixture(scope='function')
# def last_result():
#     return case_result


# 判断运行结果并处理
# @pytest.fixture(scope='function')
# def login_teardown():
#     CacheControl("cookies").cache = ""
#     yield
#     # 判断登录用例是否通过
#     if case_result == 'passed':
#         cookies_ = driver.get_cookies()
#         CacheControl("cookies").cache = str(cookies_)
#     else:
#         pytest.exit("登录用例失败！强制停止测试！")


# @pytest.fixture(scope='session')
# def debug_browser():
#     """
#     定义指定chrome账号浏览器驱动
#     """
#     global debug_driver
#     if RunConfig.driver_type == 'chrome':
#         options = webdriver.ChromeOptions()
#         options.add_argument(r'--user-common-dir=C:\Users\Admin\Desktop\AutoTest\maap_v1\chromeData')
#         options.add_argument("--profile-directory=Profile 22")
#         debug_driver = webdriver.Chrome(options=options)
#     else:
#         raise NameError("调试模式需使用Chrome浏览器")
#     # 所有用例执行接受后关闭浏览器
#     yield debug_driver
#     debug_driver.quit()
#     print("\ntest end!")
#     return debug_driver
